var Advice = {
  mixin: function() {
    for (var i=0; i < arguments.length; i++) {
      Object.extend(arguments[i], this.methods);
    };
  },
  methods: {
    methods: {},
    before: function(method, callee) {
      this._setAdvice('before', method, callee);
    },
    after: function(method, callee) {
      this._setAdvice('after', method, callee);
    },
    around: function(method, callee) {
      if (!this.methods[method]) this._aliasMethod(method);
      this.methods[method].before.unshift(callee);
      this.methods[method].after.push(callee);
    },
    _setAdvice: function(location, method, callee) {
      if (!this.methods[method]) this._aliasMethod(method);
      this.methods[method][location].push(callee);
    },
    _aliasMethod: function(method) {
      this.methods[method] = {original: this[method], before: [], after:[]};
      this[method] = function() {
        this._callMethodWithAdvice(method, arguments);
      };
    },
    _callMethodWithAdvice: function(method, args) {       
      this._applyToEach(this.methods[method].before, args);
      this.methods[method].original.apply(this, args);
      this._applyToEach(this.methods[method].after, args);
    },
    _applyToEach: function(methods, args) {
      for (var i = 0; i < methods.length; i++) {
        methods[i].apply(this, args);
      }
    }
  }
};